fix(gc): restore the census-pinned carrier expression in the shape scanner - #8918
Merged
Conversation
…anner #8899 lifted `descriptor.old_carrier || descriptor.cache_carrier` into a `let is_carrier` binding for its memo key. Semantically inert, but `shape_descriptor_census.py` pins the whole two-armed expression on purpose, so `lint` has been red on main since it landed — and the census's own self-test, which sabotages that exact literal via a no-op-on-absent `str.replace`, was disarmed at the same time.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe change restores the literal carrier predicate in ChangesCarrier census pin
Estimated code review effort: 1 (Trivial) | ~5 minutes ✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
proggeramlug
added a commit
that referenced
this pull request
Aug 28, 2026
proggeramlug
pushed a commit
to proggeramlug/perry
that referenced
this pull request
Aug 28, 2026
…plit the file - `lookup` collided with `buffer/view.rs::lookup` in the root-holder gate's loose IDENT graph, which is module-blind past depth 0, so view.rs's body text (containing VIEW_REGISTRY) counted as reachable and the holder flipped to COVERED. Renamed to `lookup_ways`. - `test_clear_shape_table` dropped every descriptor box while invalidating nothing, leaving dangling ways for any clear-then-lookup in tests. - Extracted the cfg(test) helpers to a sibling file (2057 -> 1933 lines). - Re-applied PerryTS#8918's census-pinned carrier literal, which this branch's merge had reverted.
proggeramlug
added a commit
that referenced
this pull request
Aug 28, 2026
…ble (#8917) * perf(shapes): direct-mapped cache in front of the shape-descriptor table shape_descriptor_by_id is on the hot property path — it and shape_descriptor_ensure_with_generation are ~13% of main-thread samples between them on a dynamic property loop — and every call paid a TLS fetch, a RefCell borrow and a hash probe to reach a record whose address never moves. Box<ShapeDescriptor> is stable across rehash, so a 256-way direct-mapped cache holds the record's address and a hit is mask, compare, deref. 4 KiB per thread, fixed. Caches the ADDRESS, not a copy: records are mutated in place (old_carrier, cache_carrier, keys after evacuation), so a cached copy would go quietly stale. Epoch invalidation is selective. Removal frees the box, and one insert path can replace a live id with a fresh box; both bump. A fresh-id insert deliberately does not — it cannot invalidate an existing way, and bumping there would flush the cache on every shape creation. Measured (idle host, min of 7): overwrite-only 1088ms -> 955ms, -12.2%; delete-heavy 1222ms -> 1187ms. Short of the 13% of samples because only by_id is served; ensure_with_generation still probes. Perry remains ~50x node on this loop — the rest is js_array_get_f64 (324) and try_read_tracked_gc_header (307), untouched here. Invalidation test sabotage-checked: a stale way is a dangling pointer to a dropped box, not a wrong answer. Suite 2759 passed. * fix(shapes): unblock the lookup cache — rename the colliding ident, split the file - `lookup` collided with `buffer/view.rs::lookup` in the root-holder gate's loose IDENT graph, which is module-blind past depth 0, so view.rs's body text (containing VIEW_REGISTRY) counted as reachable and the holder flipped to COVERED. Renamed to `lookup_ways`. - `test_clear_shape_table` dropped every descriptor box while invalidating nothing, leaving dangling ways for any clear-then-lookup in tests. - Extracted the cfg(test) helpers to a sibling file (2057 -> 1933 lines). - Re-applied #8918's census-pinned carrier literal, which this branch's merge had reverted. --------- Co-authored-by: Ralph Küpper <ralph3@skelpo.com> Co-authored-by: Ralph Küpper <ralph@skelpo.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
linthas been red onmainsince #8899 landed, and stayed red across #8900 and #8901. This restores it.What broke
#8899 lifted
descriptor.old_carrier || descriptor.cache_carrierout of theifinscan_shape_table_rekey_mutinto alet is_carrier = …binding, so the memo could key on it. The change is semantically inert —is_carrieris that expression.But
scripts/shape_descriptor_census.pypins the whole two-armed expression on purpose, and says so:So the pin stopped matching and the census started raising
CensusError: descriptor rooting is not gated on 'old_carrier || cache_carrier'.The second-order damage
The census's own self-test sabotages that exact literal:
str.replacedoes nothing when the string is absent. After #8899 that sabotage replaced nothing, so the "un-gated into an unconditional table root" case was passing without testing anything. A sabotage test that patches a string literal goes vacuous the moment the source is reworded, and nothing reports it.The fix
The condition is written out again at the decision site, with a comment saying why it must stay literal.
is_carrierremains and still keys the memo. One line of behaviour change: none.Attribution
Validation
scripts/run_lint_gates.sh— all 53 gates pass (compile tier skipped). Runtime 2759/0 underRUST_TEST_THREADS=1. Census exits 0, and its self-test sabotage is non-vacuous again.How this got merged
I audited #8899 by running the five gates that looked topically relevant to a GC diff and merged on that.
run_lint_gates.shexists precisely to stop this, derives its list fromtest.ymlat run time, and its header documents the same failure mode taking down five gates in one day on 2026-08-17. I should have run it; I now do.Summary by CodeRabbit
Bug Fixes
Documentation